1 现象
Win10 NodeJS 0.12.12 使用fekit 最新版 0.2.141 本地开发
fekit server -m package_b2c_admin/tests/mock.conf
fekit进程内存很快会涨到1G以上,基本上就是卡死状态,浏览器刷不出来静态资源
可以通过如上命令起本地server,多次刷新带较多静态资源的页面,同时监控进程占用的内存
2 解决办法
- 切换NodeJS版本
- 实测Node 0.10.42, Node 0.10.43, Node 5.9.0 无此问题
- Node 4.3.0, 4,4.0, 0.12.12 有此问题
- 推测,相关的大版本应该都有问题
- 简单调整fekit mock.js代码
- 修改FEKIT_HOME/lib/middleware/mock.js 将sandbox 的定义提到闭包外面来
- 进一步优化fekit mock.js代码
- 修改FEKIT_HOME/lib/middleware/mock.js 将mock_file()的结果缓存起来,如果被更新了才进行重新计算。
3 原因分析
原因这里就不分析了,记一次 Node.js 应用内存暴涨分析,这个小伙伴已经分析的很不错了。
4 排查过程
4.1 不带mock信息
无此问题,内存在100M上下波动,正常
4.2 与0.2.85 比较,发现代码几乎无差别,改动的代码对此几乎无影响
4.3 在FEKIT_HOME/lib/command/server.js中进行中间件切除
发现去掉mock后 .use(middleware.mock(options)) 问题基本解决
4.4 上面两条有点相互矛盾
4.5 通过node-inspector 调试
node-debug "C:\Users\liao.zhang\AppData\Roaming\nvm\v4.3.0\node_modules\fekit\bin\fekit" server -m package_b2c_admin/tests/mock.conf
node --debug-brk "C:\Users\liao.zhang\AppData\Roaming\nvm\v4.3.0\node_modules\fekit\bin\fekit" server -m package_b2c_admin/tests/mock.conf
查看profile数据,发现fekit占用的内容并不多
4.6 不管上面的矛盾
看看这里面闭包比较多的地方,分片注释,可以定位到
try {
vm.runInNewContext(exjson(mock_file()), sandbox);
} catch (err) {
sandbox.module.exports = {};
utils.logger.error("mock 配置文件出错 " + (err.toString()));
}
JavaScript虚拟机内存没有被释放,调用的次数又太多(每一个请求都调用),所以内存爆了
4.7 再回来
可以说已经不是fekit的问题了,85的代码和141的代码一样的时候,也会有这样的问题
4.8 NodeJS文档
大致测试,0.10.xx 应该没有这个问题;0.11.x,0.12.xx, 4.4.0 有这个问题,法不责众,我们回过头来看看fekit。
4.9 换个姿势
上面说可以说是不是fekit的问题,因为NodeJS也有问题,再来看看fekit到底有没有问题
module.exports = function(options) {
var mock_file;
if (!(options.mock && utils.path.exists(options.mock))) {
return noop;
}
utils.logger.log("成功加载 mock 配置 " + options.mock);
mock_file = utils.file.io.readbymtime(options.mock);
return function(req, res, next) {
var action, key, pattern, pieces, result, rule, rules, sandbox, url, _i, _len, _ref;
sandbox = {
module: {
exports: {}
}
};
try {
vm.runInNewContext(exjson(mock_file()), sandbox);
} catch (err) {
sandbox.module.exports = {};
utils.logger.error("mock 配置文件出错 " + (err.toString()));
}
//.......
}
上面的代码是fekit mock 中间件的主要代码,这片代码会在每个请求到达的时候被执行;可以看到在闭包里面申明了,一个沙盒变量,每次请求都会申请这么一片内存。
对于mock,在每次请求时内容不一样的情况是不可能事件,这里可以有两种解决办法:
1、将执行前的文件内容进行缓存,判断如果一样,就不执行了,执行了也没啥意义
2、直接申请同一片内存,后面的复写前面的,刷了就刷了
先用第二条改改试试效果。
module.exports = function(options) {
var mock_file,sandbox;
if (!(options.mock && utils.path.exists(options.mock))) {
return noop;
}
utils.logger.log("成功加载 mock 配置 " + options.mock);
mock_file = utils.file.io.readbymtime(options.mock);
sandbox = {
module: {
exports: {}
}
};
return function(req, res, next) {
var action, key, pattern, pieces, result, rule, rules, url, _i, _len, _ref;
try {
vm.runInNewContext(exjson(mock_file()), sandbox);
} catch (err) {
sandbox.module.exports = {};
utils.logger.error("mock 配置文件出错 " + (err.toString()));
}
//.......
}
再来看看配合缓存结果,以空间换时间的方法:
module.exports = function(options) {
var mock_file,sandbox,lastMockConfig = "";
if (!(options.mock && utils.path.exists(options.mock))) {
return noop;
}
sandbox = {
module: {
exports: {}
}
};
utils.logger.log("成功加载 mock 配置 " + options.mock);
mock_file = utils.file.io.readbymtime(options.mock);
return function(req, res, next) {
var action, key, pattern, pieces, result, rule, rules, url, _i, _len, _ref;
var thisMockConfig = mock_file();
//读文件都有缓存,计算自然也可以有,重复计算也没有意义
if(thisMockConfig !== lastMockConfig){
lastMockConfig = thisMockConfig;
try {
vm.runInNewContext(exjson(thisMockConfig), sandbox);
} catch (err) {
sandbox.module.exports = {};
utils.logger.error("mock 配置文件出错 " + (err.toString()));
}
}
// ......
}
于是这里的内存泄露问题得到解决。
5 参考文档
- 记一次 Node.js 应用内存暴涨分析
- 记一次 Node.js 应用内存暴涨分析
- 使用Node.js的VM执行脚本之后,沙箱所占的内存没有被清掉
- Node内存泄漏专题
- NodeJS内存泄露的几种情况及解决方案
- Memory leak patterns in JavaScript
- 浅谈V8引擎中的垃圾回收机制
- 深入理解Node.js中的垃圾回收和内存泄漏的捕获
- Node.js如何应对内存泄露?
- node-inspector
- 查看 Node.js 中的内存泄露
- NodeJS调试GC及内存暴涨的分析
- 使用Chrome+node-inspector查找NodeJS内存泄漏
- The node.js Profiling Guide that Hasn’t Existed - Profiling node.js Applications
- The node.js Profiling Guide that Hasn’t Existed - Finding a potential memory leak using memwatch
- The node.js Profiling Guide that Hasn’t Existed - Finding The Cause of a Memory Leak Using Heap Snapshots
最后更新: 2022年03月02日 03:32
原始链接: http://rawbin-.github.io/dev-tools/fekit/2016-01-14-fekit-mock-memleak/